/**
 * 
 */
package gui;

import game.Game;
import gfx.GraphicImage;

import io.ImportImage;

import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.HashMap;

import javax.swing.WindowConstants;

import network.GameClient;

/**
 * @author Jean-Baptiste BORIC
 *
 */
public class WindowPlayer extends WindowInGame implements MouseListener, MouseMotionListener {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	private int cursorPositionX;
	private int cursorPositionY;

	/**
	 * @param width
	 * @param height
	 * @param game
	 * @param images
	 */
	public WindowPlayer(int width, int height, Game game,
			HashMap<String, GraphicImage> images) {
		super(width, height, game, images);
		
		this.frameBuffer.addMouseMotionListener(this);
		this.addMouseMotionListener(this);
		this.frameBuffer.addMouseListener(this);
	}
	
	public void setCursorPosition(int x, int y) {
		if(x >= this.game.getMap().getWidth() ||y >= this.game.getMap().getHeight()) {
			x = -1;
			y = -1;
		}
		this.cursorPositionX = x;
		this.cursorPositionY = y;
	}
	
	public void setCursorPositionMouse(int mx, int my) {
		mx = (int) ((double)(mx) / this.getWidth() * this.frameBuffer.getVirtualWidth());
		my = (int) ((double)(my) / this.getHeight() * this.frameBuffer.getVirtualHeight());
		if(this.game.isStarted()) {
			GraphicImage tilemapImage = this.images.get(this.game.getTileTypes().iterator().next().getImage());
			int tileWidth = tilemapImage.getWidth() / tilemapImage.getNbColumns();
			int tileHeight = tilemapImage.getHeight() / tilemapImage.getNbRows();
			
			mx -= this.getCameraX();
			my -= this.getCameraY();
			mx /= tileWidth;
			my /= tileHeight;
			this.setCursorPosition(mx, my);
		}
	}
	
	public void drawCursor() {
		if(this.game.isStarted()) {
			GraphicImage tilemapImage = this.images.get(this.game.getTileTypes().iterator().next().getImage());
			int tileWidth = tilemapImage.getWidth() / tilemapImage.getNbColumns();
			int tileHeight = tilemapImage.getHeight() / tilemapImage.getNbRows();
			
			this.frameBuffer.fillRect(this.cursorPositionX*tileWidth, this.cursorPositionY*tileHeight, tileWidth, tileHeight, new Color(1.f, 1.f, 1.f, 0.5f));
		}
	}
	
	/**
	 * Clip the camera so it doesn't move outside the tilemap 
	 * @param x Camera X coordinate
	 * @param y Camera Y coordinate
	 */
	private void clipCamera(int x, int y) {
		if(this.game.isStarted()) {
			GraphicImage tilemapImage = this.images.get(this.game.getTileTypes().iterator().next().getImage());
			int mapWidth = this.game.getMap().getWidth() * tilemapImage.getWidth() / tilemapImage.getNbColumns();
			int mapHeight = this.game.getMap().getHeight() * tilemapImage.getHeight() / tilemapImage.getNbRows();
			
			// Clip on right edge
			if(x <  - (mapWidth-this.frameBuffer.getVirtualWidth())) {
				x = - (mapWidth-this.frameBuffer.getVirtualWidth());
			}
			// Clip on left edge
			if(x > 0) {
				x = 0;
			}
			
			// Clip on bottom edge
			if(y <  - (mapHeight-this.frameBuffer.getVirtualHeight())) {
				y = - (mapHeight-this.frameBuffer.getVirtualHeight());
			}
			// Clip on top edge
			if(y > 0) {
				y = 0;
			}

			this.setCamera(x, y);
		}
	}
	
	/**
	 * Scroll the camera as necessary based on the mouse position
	 * @param mx Mouse's X position
	 * @param my Mouse's Y position
	 */
	private void moveCameraMouse(int mx, int my) {
		final double threshold = 1.0/8.0;
		double speedFactX = (this.getWidth() * threshold);
		double speedFactY = (this.getHeight()  * threshold);
		
		double speedX = 0;
		double speedY = 0;
		
		// Left scrolling
		if(mx < speedFactX) {
			speedX = 1 - ((mx) / (speedFactX));
		}
		// Right scrolling
		else if(this.getWidth() - mx < speedFactX) {
			speedX = - (1 - ((this.frameBuffer.getWidth() - mx) / (speedFactX)));
		}
		// Top scrolling
		if(my < speedFactY) {
			speedY = 1 - ((my) / (speedFactY));
		}
		// Bottom scrolling
		else if(this.getHeight() - my < speedFactY) {
			speedY = - (1 - ((this.frameBuffer.getHeight() - my) / (speedFactY)));
			
		}
		
		speedX *= 4;
		speedY *= 4;
		
		this.clipCamera((int)(this.getCameraX() + speedX), (int)(this.getCameraY() + speedY));
	}
	
	/**
	 * Get mouse coordinates relative to framebuffer
	 * @return Mouse coordinates, or null if mouse is outside framebuffer
	 */
	private Integer[] getMouseCoordinates() {
		Integer[] result = null;
		
		// Get mouse position in framebuffer coordinates
		PointerInfo a = MouseInfo.getPointerInfo();
		Point b = a.getLocation();
		int x = (int) b.getX() - this.getX() - (this.getWidth() - this.frameBuffer.getWidth());
		int y = (int) b.getY() - this.getY() - (this.getHeight() - this.frameBuffer.getHeight());;
		
		// If mouse is inside framebuffer
		if(x > 0 && y > 0 && x < this.frameBuffer.getWidth() && y < this.frameBuffer.getHeight()) {
			result =  new Integer[2];
			result[0] = x;
			result[1] = y;
		}
		return result;
	}

	//
	// User interaction
	//

	@Override
	public void mouseDragged(MouseEvent arg0) {
		// Ignore dragging
		
	}

	@Override
	public void mouseMoved(MouseEvent arg0) {

	}
	
	@Override
	public void mouseClicked(MouseEvent arg0) {
		
	}
	
	@Override
	public void mouseEntered(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void mouseExited(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void mousePressed(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void mouseReleased(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}
	
	
	//
	// Main
	//
	
	/**
	 * Fonctions tests du client.
	 * 
	 * Le client ira automatiquement se connecter sur l'adresse de serverHostname 
	 * (localhost par défaut).
	 * 
	 * @param args
	 */
	public static void main(String[] args) throws UnknownHostException, IOException {
		
		final String serverHostname = "localhost";
		
		// Load images
		ImportImage importImages = new ImportImage(new FileInputStream("data/database/image.tsv"));
		HashMap<String, GraphicImage> images = importImages.read();
		
		// Connect to game
		Socket s = new Socket(InetAddress.getByName(serverHostname), 34123);
		GameClient client = new GameClient(s);
		WindowPlayer w = new WindowPlayer(640, 480, client, images);
		
		w.setVisible(true);
		w.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

		while(true) {
			client.pumpCommmands();

			// Scroll map as necessary
			Integer[] scroll = w.getMouseCoordinates();
			if(scroll != null) {
				w.moveCameraMouse(scroll[0], scroll[1]);
				w.setCursorPositionMouse(scroll[0], scroll[1]);
			}
			
			// Draw everything
			w.updateGraphics();
			w.drawCursor();
			w.swapBuffers();
			
			// Sleep a little
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}
	}
}
